﻿using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Threading;

namespace FftForSpectrum
{
    public partial class FftForSpectumControl : UserControl
    {
        private delegate void StartDelegate();

        private delegate void StopDelegate();

        /// <summary>
        /// 柱形数量
        /// <para>当柱形界面柱形数量模式为NUM时有效</para>
        /// </summary>
        private int _barcount = 10;

        /// <summary>
        /// 柱形间隙
        /// </summary>
        private int _barspacing = 1;

        /// <summary>
        /// 柱形宽度
        /// </summary>
        private int _barwidth = 4;

        /// <summary>
        /// 最大db值，及Y轴最大值
        /// </summary>
        private int _maxdb = 120;

        /// <summary>
        /// 柱形数量模式
        /// <para>AUTO为自动计算；NUM为固定值</para>
        /// </summary>
        private BarCountModel _barcountmodel = BarCountModel.AUTO;

        /// <summary>
        /// 动画计时器
        /// </summary>
        private System.Timers.Timer _ontimesObj = null;

        /// <summary>
        /// 动画计时器时间间隔
        /// </summary>
        private int _ontimespeet = 100;

        /// <summary>
        /// 动画变值步长
        /// </summary>
        private int _animespeet = 2;

        /// <summary>
        /// 柱形对象数组
        /// </summary>
        private BarClass[] _bars = null;

        /// <summary>
        /// 顶点对象数组
        /// </summary>
        private AcmeClass[] _acmes = null;

        /// <summary>
        /// 线状的点
        /// </summary>
        private Point[] _lines = null;

        /// <summary>
        /// 线状点的倍数步长
        /// </summary>
        private int _linepointspeet = 5;

        /// <summary>
        /// 线状时默认的Y中间点
        /// </summary>
        private int _linepointdefY = 0;

        /// <summary>
        /// 是否显示顶点
        /// </summary>
        private bool _showacme = true;

        /// <summary>
        /// 顶点的高度
        /// </summary>
        private int _acmeheight = 1;

        /// <summary>
        /// 顶点动画的调整步长
        /// </summary>
        private int _acmespeet = 6;

        /// <summary>
        /// 是否初始化标识
        /// </summary>
        private bool _aisinit = false;

        /// <summary>
        /// 显示的方式
        /// </summary>
        private ShowType _showtype = ShowType.BAR;

        /// <summary>
        /// 画柱形的渐变画笔
        /// </summary>
        private LinearGradientBrush _barbrush = null;

        /// <summary>
        /// 柱形高处（高音域）颜色
        /// </summary>
        private Color _barcolorG = Color.Red;

        /// <summary>
        /// 柱形中处（中音域）颜色
        /// </summary>
        private Color _barcolorZ = Color.Yellow;

        /// <summary>
        /// 柱形低处（低音域）颜色
        /// </summary>
        private Color _barcolorD = Color.Green;

        /// <summary>
        /// 顶部方块的颜色
        /// </summary>
        private Color _acmecolor = ColorTranslator.FromHtml("#BAB8B8");

        /// <summary>
        /// 顶部方块的画笔
        /// </summary>
        private SolidBrush _acmebrush = null;

        /// <summary>
        /// 线形的颜色
        /// </summary>
        private Color _linecolor = Color.White;

        /// <summary>
        /// 线形的画笔
        /// </summary>
        private Pen _linepen = null;

        /// <summary>
        /// 顶点动画的调整步长
        /// </summary>
        [DefaultValue(6), Description("顶点动画的调整步长")]
        public int AcmeSpeet
        {
            get { return _acmespeet; }
            set 
            {
                if (value < 3)
                {
                    _acmespeet = 3;
                }
                else if (value > 20)
                {
                    _acmespeet = 20;
                }
                else
                {
                    _acmespeet = value;
                }
            }
        }

        /// <summary>
        /// 显示的方式
        /// </summary>
        [DefaultValue(ShowType.BAR), Description("显示的方式")]
        public ShowType ShowTyp
        {
            get { return _showtype; }
            set 
            {
                _showtype = value;
                _aisinit = false;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 柱形高处（高音域）颜色
        /// </summary>
        [DefaultValue(typeof(Color), "Red"), Description("柱形高处（高音域）颜色")]
        public Color BarColorG
        {
            get { return _barcolorG; }
            set { _barcolorG = value; }
        }

        /// <summary>
        /// 柱形中处（中音域）颜色
        /// </summary>
        [DefaultValue(typeof(Color), "Yellow"), Description("柱形中处（中音域）颜色")]
        public Color BarColorZ
        {
            get { return _barcolorZ; }
            set { _barcolorZ = value; }
        }

        /// <summary>
        /// 柱形低处（低音域）颜色
        /// </summary>
        [DefaultValue(typeof(Color),"Green"), Description("柱形低处（低音域）颜色")]
        public Color BarColorD
        {
            get { return _barcolorD; }
            set { _barcolorD = value; }
        }

        /// <summary>
        /// 顶部方块的颜色
        /// </summary>
        [DefaultValue(typeof(Color),"#BAB8B8"), Description("顶部方块的颜色")]
        public Color AcmeColor
        {
            get { return _acmecolor; }
            set { _acmecolor = value; }
        }

        /// <summary>
        /// 柱形界面时柱形数量
        /// </summary>
        [DefaultValue(10), Description("柱形界面时柱形数量")]
        public int BarCount
        {
            get { return _barcount; }
            set
            {
                if (value < 1)
                {
                    _barcount = 1;
                }
                else
                {
                    _barcount = value;
                }
            }
        }

        /// <summary>
        /// 柱形界面柱形数量模式
        /// </summary>
        [DefaultValue(BarCountModel.AUTO), Description("柱形界面柱形数量模式")]
        public BarCountModel BarCountModel
        {
            get { return _barcountmodel; }
            set { _barcountmodel = value; }
        }

        /// <summary>
        /// 是否显示顶点
        /// </summary>
        [DefaultValue(false), Description("是否显示顶点")]
        public bool ShowAcme
        {
            get { return _showacme; }
            set { _showacme = value; }
        }

        /// <summary>
        /// 线形的颜色
        /// </summary>
        [DefaultValue(typeof(Color), "White"), Description("线形的颜色")]
        public Color LineColor
        {
            get { return _linecolor; }
            set { _linecolor = value; }
        }

        public FftForSpectumControl()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
        }

        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            switch (_showtype)
            {
                case ShowType.BAR:
                    {
                        int ibarnum = 0;
                        if (_barcountmodel == BarCountModel.AUTO)
                        {
                            int ibarobjwidth = _barwidth + _barspacing;
                            _barcount = ibarnum = this.Size.Width / ibarobjwidth;
                        }
                        else
                        {
                            ibarnum = _barcount;
                            _barwidth = this.Width / _barcount - _barspacing;
                        }

                        _bars = new BarClass[ibarnum];
                        if (_showacme)
                        {
                            _acmes = new AcmeClass[ibarnum];
                        }
                        for (int i = 0; i < _bars.Length; i++)
                        {
                            BarClass ibarobj = _bars[i] = new BarClass();
                            ibarobj.Value = _showacme ? (_maxdb - _acmeheight - _barspacing) : _maxdb;
                            ibarobj.MaxValue = this.Size.Height;
                            if (_showacme)
                            {
                                AcmeClass iacmeobj = _acmes[i] = new AcmeClass();
                                iacmeobj.Value = 0;
                                iacmeobj.Speet = _acmespeet;
                            }
                        }

                        Color[] icolors = new Color[] { _barcolorG, _barcolorZ, _barcolorD };
                        float[] ipos = new float[] { 0.0f, 0.3f, 1.0f };
                        ColorBlend icolorblend = new ColorBlend();
                        icolorblend.Colors = icolors;
                        icolorblend.Positions = ipos;

                        Rectangle irec = new Rectangle(0, 0, _barwidth, this.Size.Height);

                        _barbrush = new LinearGradientBrush(irec, Color.White, Color.Black, LinearGradientMode.Vertical);
                        _barbrush.InterpolationColors = icolorblend;

                        _acmebrush = new SolidBrush(_acmecolor);
                        break;
                    }
                case ShowType.LINE:
                    {
                        int ilps = this.Size.Width / _linepointspeet;
                        int ilpy = _linepointdefY = this.Size.Height / 2;
                        _lines = new Point[ilps];
                        for (int i = 0; i < _lines.Length; i++)
                        {
                            Point ipobj = new Point();
                            ipobj.Y = ilpy;
                            ipobj.X = i + i * _linepointspeet;
                            _lines[i] = ipobj;
                        }

                        _linepen = new Pen(_linecolor);
                        break;
                    }
            }
            _aisinit = true;
            Start();
        }

        /// <summary>
        /// 开始动画
        /// </summary>
        public void Start()
        {
            if (this.InvokeRequired)
            {
                StartDelegate iSD = new StartDelegate(this.Start);
                this.Invoke(iSD);
                return;
            }

            if (_ontimesObj == null)
            {
                _ontimesObj = new System.Timers.Timer(_ontimespeet);
                _ontimesObj.AutoReset = true;
                _ontimesObj.Elapsed += new System.Timers.ElapsedEventHandler(_ontimesObj_Elapsed);
                _ontimesObj.Start();
            }
        }

        /// <summary>
        /// 计时器事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _ontimesObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!_aisinit)
            {
                return;
            }
            switch (_showtype)
            {
                case ShowType.BAR:
                    {
                        Monitor.Enter(_bars);
                        Monitor.Enter(_acmes);
                        int ibl = _bars.Length;
                        for (int i = 0; i < ibl; i++)
                        {
                            BarClass ibarobj = _bars[i];
                            if (ibarobj.Value > 0)
                            {
                                ibarobj.Value = ibarobj.Value - _animespeet;
                            }
                            else
                            {
                                ibarobj.Value = 0;
                            }
                            if (_showacme)
                            {
                                AcmeClass iacmeobj = _acmes[i];
                                if (iacmeobj.Speet > 0)
                                {
                                    iacmeobj.Speet--;
                                }
                                else
                                {
                                    iacmeobj.Value = iacmeobj.Value - _animespeet;
                                }
                            }
                        }
                        Monitor.Exit(_bars);
                        Monitor.Exit(_acmes);
                        break;
                    }
                case ShowType.LINE:
                    {
                        Monitor.Enter(_lines);
                        int ill = _lines.Length;
                        for (int i = 0; i < ill; i++)
                        {
                            Point ipobj = _lines[i];
                            if (ipobj.Y > _linepointdefY)
                            {
                                ipobj.Y--;
                            }
                            if (ipobj.Y < _linepointdefY)
                            {
                                ipobj.Y++;
                            }
                            _lines[i] = ipobj;
                        }
                        Monitor.Exit(_lines);
                        break;
                    }
            }

            this.Invalidate();
        }

        /// <summary>
        /// 动画停止
        /// </summary>
        public void Stop()
        {
            if (this.InvokeRequired)
            {
                StopDelegate iSD = new StopDelegate(this.Stop);
                this.Invoke(iSD);
                return;
            }

            if(_ontimesObj!=null)
            {
                _ontimesObj.Stop();
                _ontimesObj.Dispose();
                _ontimesObj = null;
            }
        }

        /// <summary>
        /// 设置FFT数据
        /// </summary>
        /// <param name="fdata">FFT数据数组</param>
        public void SetFFTData(int[] fdata)
        {
            if (fdata == null)
            {
                return;
            }
            int ilen = 0;
            switch (_showtype)
            {
                case ShowType.BAR:
                    {
                        ilen = fdata.Length <= _bars.Length ? fdata.Length : _bars.Length;
                        for (int i = 0; i < ilen; i++)
                        {
                            if (_showacme)
                            {
                                AcmeClass iacmeobj = _acmes[i];
                                int ivalue = fdata[i] > _maxdb ? _maxdb : fdata[i];
                                iacmeobj.Value = ivalue < iacmeobj.Value ? iacmeobj.Value : ivalue;
                                iacmeobj.Speet = ivalue < iacmeobj.Value ? iacmeobj.Speet : _acmespeet; ;
                            }

                            BarClass ibarobj = _bars[i];
                            ibarobj.Value = fdata[i] > (_showacme ? _maxdb - _acmeheight - _barspacing : _maxdb)
                                ? (_showacme ? _maxdb - _acmeheight - _barspacing : _maxdb) : fdata[i];
                        }
                        break;
                    }
                case ShowType.LINE:
                    {
                        ilen = fdata.Length <= _lines.Length ? fdata.Length : _lines.Length;
                        for (int i = 0; i < ilen; i++)
                        {
                            Point ipobj = _lines[i];
                            decimal iv = fdata[i] > _maxdb ? _maxdb : fdata[i];
                            decimal ia = (iv / ((decimal)_maxdb));
                            int iY = (int)((ia > 1 ? 1 : ia) * this.Size.Height);
                            ipobj.Y = this.Size.Height - iY;
                            _lines[i] = ipobj;
                            //Debug.WriteLine("X:" + ipobj.X.ToString() + ",Y:" + ipobj.Y.ToString());
                        }
                        break;
                    }
            }
            
            this.Invalidate();
        }

        /// <summary>
        /// OnPaint事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics ig = e.Graphics;
            Brush iBrush = new SolidBrush(Color.Transparent);
            Rectangle iRec=new Rectangle(0,0,this.Size.Width,this.Size.Height);
            ig.FillRectangle(iBrush, iRec);

            if (!_aisinit)
            {
                Init();
            }
            switch (_showtype)
            {
                case ShowType.BAR:
                    {
                        DrawBar(ig, e.ClipRectangle);
                        break;
                    }
                case ShowType.LINE:
                    {
                        DrawLine(ig, e.ClipRectangle);
                        break;
                    }
            }
            
        }

        /// <summary>
        /// 画柱形
        /// </summary>
        /// <param name="e">画布对象</param>
        /// <param name="rec">区域对象</param>
        private void DrawBar(Graphics e,Rectangle rec)
        {
            decimal ia = 0;
            int iheight = 0;
            bool iisEqual = true;
            for (int i = 0; i < _bars.Length; i++)
            {
                BarClass ibarobj = _bars[i];
                if (_showacme)
                {
                    AcmeClass iacmeobj = _acmes[i];
                    if (iacmeobj.Value <= 0)
                    {
                        continue;
                    }

                    ia = (((decimal)iacmeobj.Value) / ((decimal)_maxdb));
                    iheight = (int)((ia > 1 ? 1 : ia) * this.Size.Height);
                    Rectangle ibarrec1 = new Rectangle((((i + 1) * 1) + (i * _barwidth)), this.Size.Height - iheight, _barwidth, _acmeheight);
                    e.FillRectangle(_acmebrush, ibarrec1);

                    iisEqual = ibarobj.Value == iacmeobj.Value ? true : false;
                }
                
                if (ibarobj.Value <= 0)
                {
                    continue;
                }
                if ((!_showacme) || (!iisEqual))
                {
                    ia = (((decimal)ibarobj.Value) / ((decimal)_maxdb));
                    iheight = (int)((ia > 1 ? 1 : ia) * this.Size.Height);
                }
                else
                {
                    iheight = iheight - _acmeheight - _barspacing;
                }
                Rectangle ibarrec2 = new Rectangle((((i + 1) * 1) + (i * _barwidth)), this.Size.Height - iheight, _barwidth, iheight);
                e.FillRectangle(_barbrush, ibarrec2);
            }
        }

        /// <summary>
        /// 画线性
        /// </summary>
        /// <param name="e"></param>
        /// <param name="rec"></param>
        private void DrawLine(Graphics e, Rectangle rec)
        {
            e.DrawLines(_linepen, _lines);
        }
    }
}
